﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using AWLoadTesterLibrary;

namespace AWLoadTester
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private object syncObj;
        private LoadTestRunOptions MainOptions;
        private Thread MainTestThread;
        public List<Thread> AllTestThreads { get; set; }
        private RegistryUtil MainReg { get; set; }
        private System.Timers.Timer StatsTimer { get; set; }
        Process CurrentProcess { get; set; }

        // Browser
        private System.Diagnostics.Stopwatch CurrentStopWatch { get; set; }
        private int LoadCount = 0;

        public MainWindow()
        {
            InitializeComponent();

            CurrentStopWatch = new System.Diagnostics.Stopwatch();

            AllTestThreads = new List<Thread>();

            MainReg = new RegistryUtil();
            MainReg.ReadAll();

            MainOptions = new LoadTestRunOptions();
            MainOptions.URL = "http://bing.com/";
            MainOptions.UsersMin = "1";     //
            MainOptions.ConConnections = "1";    // 
            MainOptions.BrowseTime = "1"; // Seconds
            MainOptions.TestTime = "1";   // Minutes
            MainOptions.ThreadList = AllTestThreads;

            MainOptions.Read(MainReg);

            MainGrid.DataContext = MainOptions;
            StatsMainGrid.DataContext = MainOptions;
            LogMainGrid.DataContext = MainOptions;

            syncObj = new object();
            CurrentProcess = Process.GetCurrentProcess();

            // Setup the timer for the other tests
            StatsTimer = new System.Timers.Timer();
            StatsTimer.Interval = 10*1000;
            StatsTimer.AutoReset = true;
            StatsTimer.Elapsed += new System.Timers.ElapsedEventHandler((e, x) =>
            {
                int count = AllTestThreads.Count;

                ThreadCountTextBox.Dispatcher.BeginInvoke(new Action(() => { ThreadCountTextBox.Content = count.ToString(); }));
            });
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            if (MainTestThread != null && MainTestThread.IsAlive)
            {
                MessageBoxResult res = MessageBox.Show("Tests already running. End it?", "Running tests", MessageBoxButton.OKCancel, MessageBoxImage.Error);
                if (res == MessageBoxResult.OK)
                {
                    MainTestThread.Abort();
                }
                return;
            }

            // Save the settings in the registry
            MainOptions.Write(MainReg);
            MainReg.WriteAll();

            // Show the browser window if instructed
            if (MainOptions.ShowBrowser)
            {
                BrowserWindow bw = new BrowserWindow();
                bw.Location = MainOptions.URL;
                bw.Show();
            }

            if (Monitor.TryEnter(syncObj) == false)
            {
                return;
            }

            try
            {
                LoadTesterClass ltc = new LoadTesterClass();

                if (String.IsNullOrWhiteSpace(MainOptions.URL))
                {
                    MessageBox.Show("Invalid Location", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                int tempNum=0;
                if (String.IsNullOrWhiteSpace(MainOptions.UsersMin) || (Int32.TryParse(MainOptions.UsersMin, out tempNum) == false))
                {
                    MessageBox.Show("Invalid Users/Minute Value.  Please enter the number of users per minute for test", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                if (String.IsNullOrWhiteSpace(MainOptions.ConConnections) || (Int32.TryParse(MainOptions.ConConnections, out tempNum) == false))
                {
                    MessageBox.Show("Invalid connections Value.  Please enter the number of connections each user can open at a time for test", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }

                StartButton.IsEnabled = false;

                MainTestThread = new Thread(new ThreadStart(() =>
                    {
                        try
                        {
                            MainOptions.StartTestTimer();
                            LoadTestRunResults res = null;
                            ltc.Run(MainOptions, out res);

                            do
                            {
                                // Wait until the timer is done
                                Thread.Sleep(5000);

                                // Log stats
                                BuildLogs(res);

                            } while (ltc.HasActiveThreads());
                        }
                        finally
                        {
                            StartButton.Dispatcher.BeginInvoke(new Action(() => 
                            { 
                                StartButton.IsEnabled = true; 
                            }));

                            MainOptions.StopTestTimer();

                            System.Windows.MessageBox.Show("Test ended", "Complete", MessageBoxButton.OK, MessageBoxImage.Information);
                        }
                    }));
                MainTestThread.Name = "AWLoadTesterTestThread";
                MainTestThread.Start();
                AllTestThreads.Add(MainTestThread);
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("Error running tests: {0}", ex.Message), "Load Test error", 
                                                MessageBoxButton.OK, MessageBoxImage.Error);
                StartButton.IsEnabled = true;
            }
            finally
            {
                Monitor.Exit(syncObj);
               
            }
        }

        private void BuildLogs(LoadTestRunResults res)
        {
            // Clean dead threads
            //IEnumerable<Thread> tempThreads = MainOptions.ThreadList.Where(t => t.IsAlive == false);
            //MainOptions.LogText = String.Format("{0}{1} dead threads found. {2} total\n", MainOptions.LogText, tempThreads.Count(),
            //                                                MainOptions.ThreadList.Count);

            //foreach (Thread currThread in tempThreads)
            //{
            //    MainOptions.ThreadList.Remove(currThread);
            //}

            TimeSpan currTime = TimeSpan.Zero;
            DateTime minConDate = DateTime.MinValue;
            DateTime maxConDate = DateTime.MaxValue;

            foreach (LoadTestUserResult currUserRes in res.UserResults)
            {
                currTime += currUserRes.StopTime.Subtract(currUserRes.StartTime);
                if (currUserRes.StartTime < minConDate || minConDate == DateTime.MinValue)
                    minConDate = currUserRes.StartTime;

                if (currUserRes.StopTime > maxConDate || maxConDate == DateTime.MaxValue)
                    maxConDate = currUserRes.StopTime;
            }

            TimeSpan totalTime = maxConDate.Subtract(minConDate);
            double usersPerMinute = 0.0;

            try
            {
                usersPerMinute = ((double)res.UserResults.Count) / totalTime.TotalMinutes;
            }
            catch
            {

            }

            MainOptions.LogText = String.Format("{0}{1:c} combined time spent browsing by {2} users. {3:c} total test time\n{4:0.0000} user page loads/minute",
                MainOptions.LogText, currTime, res.UserResults.Count, totalTime, usersPerMinute);
        }

        private void LoadTesterWindow_Loaded(object sender, RoutedEventArgs e)
        {
            StatsTimer.Enabled = true;

            try
            {
                MainWebBrowser.Navigating += new System.Windows.Navigation.NavigatingCancelEventHandler(MainWebBrowser_Navigating);
                MainWebBrowser.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(MainWebBrowser_LoadCompleted);
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("Browser Error : {0}", ex.Message), "Error loading web site",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void outputFileBrowseButton_Click(object sender, RoutedEventArgs e)
        {
            Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog();
            ofd.DefaultExt = "xml";
            ofd.Filter = "*.xml|AW Load Tester Statistics File";
            if (String.IsNullOrWhiteSpace(OutputFileTextBox.Text) == false)
            {
                ofd.FileName = OutputFileTextBox.Text;
            }
            else
            {
                ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                ofd.FileName = "AWLoadTesterOutput.xml";
            }

            if (ofd.ShowDialog()==true)
            {
                OutputFileTextBox.Text = ofd.FileName;
            }
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                StartButton.IsEnabled = false;

                // Terminate all threads
                List<Thread> allThreadsCopy = AllTestThreads.ToList();

                foreach (Thread currThread in allThreadsCopy)
                {
                    try
                    {
                        currThread.Abort();
                        if (currThread.IsAlive == false)
                        {
                            AllTestThreads.Remove(currThread);
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(String.Format("Error terminating thread : Thread {0} : {1}", currThread.ManagedThreadId, ex.Message), 
                            "Error stopping thread", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("Error while terminating test run threads : {1}", ex.Message),
                    "Error stopping test run", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                StartButton.IsEnabled = true;
            }
        }


        private void ShowLoadString()
        {
            LocationLabel.Content = String.Format("[{0}], Loaded in {1}", MainOptions.URL, CurrentStopWatch.Elapsed);
        }

        void MainWebBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            CurrentStopWatch.Stop();
            ShowLoadString();
        }

        void MainWebBrowser_Navigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
        {
            CurrentStopWatch.Restart();
        }

        private void LoadButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                LocationLabel.Content = "Loading...";
                LoadButton.IsEnabled = false;

                Uri CurrentUri = null;

                try
                {
                    CurrentUri = new Uri(MainOptions.URL);
                }
                catch (Exception ex)
                {
                    System.Windows.MessageBox.Show(String.Format("Error parsing URL : '{0}' : {1}", MainOptions.URL, ex.Message), 
                        "Site location error", MessageBoxButton.OK, MessageBoxImage.Error);

                    return;
                }

                MainWebBrowser.Navigate(CurrentUri);

                //if (LoadCount++==0)
                //{
                //    MainWebBrowser.Navigate(CurrentUri);
                //}
                //else
                //{
                //    CurrentStopWatch.Restart();
                //    MainWebBrowser.Refresh(true);
                //    CurrentStopWatch.Stop();
                //    ShowLoadString();
                //}

                //Timing = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("Error with location : [{0}] : {1}", MainOptions.URL, ex.Message), "Error loading web site",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                LoadButton.IsEnabled = true;
                LoadButton.Content = "Load";
            }
        }
    }
}
